package org.example.service;

import org.example.config.AnalysisConfig;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Collectors;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;

import java.io.BufferedInputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
import java.util.regex.Matcher;


public class ApkProcessor {
    // 缓存正则表达式模式，避免重复编译
    private static final Pattern DEPENDENCY_PATTERN = Pattern.compile("package:([a-zA-Z0-9_]+)(?:@([0-9]+\\.[0-9]+\\.[0-9]+))?");
    
    public Map<String, String> analyzeApk(String apkPath, AnalysisConfig config) {
        try (ZipFile apk = new ZipFile(apkPath)) {
            // 检测是否包含 Flutter
            if (!isFlutterApk(apk)) return Collections.emptyMap();

            // 优化：直接在内存中处理，避免临时文件
            Map<String, byte[]> libs = extractLibsToMemory(apk, config.getPreferredArchs());

            // 提取字符串和 NOTICES 文件
            List<String> strings = extractStringsFromBinaryInMemory(libs.get("libapp.so"));
            List<String> notices = extractNoticesInMemory(apk);

            // 合并依赖项
            Map<String, String> dependencies = findDependencies(strings, notices, config.getKnownPackages());
            return dependencies;
        } catch (IOException e) {
            e.printStackTrace();
            return Collections.emptyMap();
        }
    }

    private boolean isFlutterApk(ZipFile apk) {
        return apk.stream().anyMatch(entry ->
                entry.getName().contains("libflutter.so") ||
                        entry.getName().startsWith("assets/flutter_assets/"));
    }

    // 优化：直接在内存中提取库文件，避免磁盘I/O
    private Map<String, byte[]> extractLibsToMemory(ZipFile apk, List<String> preferredArchs) {
        Map<String, byte[]> result = new HashMap<>();
        result.put("libapp.so", null);
        result.put("libflutter.so", null);

        try {
            // 收集所有候选库文件路径
            LinkedHashSet<String> libAppPaths = apk.stream()
                    .filter(e -> e.getName().startsWith("lib/") && e.getName().endsWith("/libapp.so"))
                    .map(ZipEntry::getName)
                    .collect(Collectors.toCollection(LinkedHashSet::new));

            LinkedHashSet<String> libFlutterPaths = apk.stream()
                    .filter(e -> e.getName().startsWith("lib/") && e.getName().endsWith("/libflutter.so"))
                    .map(ZipEntry::getName)
                    .collect(Collectors.toCollection(LinkedHashSet::new));

            // 提取 libapp.so 到内存
            if (!libAppPaths.isEmpty()) {
                String selectedPath = findPreferredArch(libAppPaths, preferredArchs);
                result.put("libapp.so", extractFileToMemory(apk, selectedPath));
            } else {
                System.out.println("APK 中未找到 libapp.so 文件。");
            }

            // 提取 libflutter.so 到内存
            if (!libFlutterPaths.isEmpty()) {
                String selectedPath = findPreferredArch(libFlutterPaths, preferredArchs);
                result.put("libflutter.so", extractFileToMemory(apk, selectedPath));
            } else {
                System.out.println("APK 中未找到 libflutter.so 文件。");
            }
        } catch (Exception e) {
            System.out.println("提取库文件时发生错误: " + e.getMessage());
        }
        return result;
    }

    private String findPreferredArch(LinkedHashSet<String> paths, List<String> preferredArchs) {
        // 按架构优先级查找
        for (String arch : preferredArchs) {
            String prefix = "lib/" + arch + "/";
            for (String path : paths) {
                if (path.startsWith(prefix)) {
                    return path;
                }
            }
        }
        // 返回第一个可用的路径
        return paths.iterator().next();
    }

    // 新增：将文件提取到内存而不是磁盘
    private byte[] extractFileToMemory(ZipFile apk, String entryPath) throws IOException {
        ZipEntry entry = apk.getEntry(entryPath);
        if (entry == null) return null;
        
        byte[] data = new byte[(int) entry.getSize()];
        try (InputStream is = apk.getInputStream(entry)) {
            int bytesRead = 0;
            int totalBytesRead = 0;
            while (totalBytesRead < data.length && 
                   (bytesRead = is.read(data, totalBytesRead, data.length - totalBytesRead)) != -1) {
                totalBytesRead += bytesRead;
            }
        }
        return data;
    }

    // 保留原有方法以兼容性
    private String extractFile(ZipFile apk, String entryPath, String outputDir) throws IOException {
        Path targetPath = Path.of(outputDir, entryPath);
        Files.createDirectories(targetPath.getParent());
        try (InputStream is = apk.getInputStream(apk.getEntry(entryPath))) {
            Files.copy(is, targetPath, StandardCopyOption.REPLACE_EXISTING);
        }
        return targetPath.toString();
    }

    // 优化：在内存中处理NOTICES文件
    private List<String> extractNoticesInMemory(ZipFile apk) {
        List<String> notices = new ArrayList<>();

        try {
            // 查找 NOTICES 文件
            ZipEntry noticeEntry = apk.stream()
                    .filter(e -> e.getName().startsWith("assets/flutter_assets/NOTICES"))
                    .findFirst()
                    .orElse(null);

            if (noticeEntry == null) {
                System.out.println("未找到 NOTICES 文件");
                return notices;
            }

            // 直接在内存中读取文件内容
            byte[] noticeData = extractFileToMemory(apk, noticeEntry.getName());
            if (noticeData == null) return notices;

            // 检查是否为Gzip压缩
            boolean isGzipped = noticeData.length >= 2 && 
                               (noticeData[0] & 0xFF) == 0x1F && 
                               (noticeData[1] & 0xFF) == 0x8B;

            try (InputStream is = new ByteArrayInputStream(noticeData)) {
                if (isGzipped) {
                    try (GZIPInputStream gzis = new GZIPInputStream(is);
                         BufferedReader reader = new BufferedReader(new InputStreamReader(gzis))) {
                        reader.lines().forEach(notices::add);
                    }
                } else {
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
                        reader.lines().forEach(notices::add);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("处理 NOTICES 文件时发生错误: " + e.getMessage());
        }
        return notices;
    }

    // 保留原有方法以兼容性
    private List<String> extractNotices(ZipFile apk, String outputDir) {
        List<String> notices = new ArrayList<>();
        String noticesPath = null;

        try {
            // 查找 NOTICES 文件
            ZipEntry noticeEntry = apk.stream()
                    .filter(e -> e.getName().startsWith("assets/flutter_assets/NOTICES"))
                    .findFirst()
                    .orElse(null);

            if (noticeEntry == null) {
                System.out.println("未找到 NOTICES 文件");
                return notices;
            }

            // 提取文件到输出目录
            noticesPath = extractFile(apk, noticeEntry.getName(), outputDir);
            Path noticeFile = Path.of(noticesPath);

            // 读取前两个字节检查Gzip头
            byte[] header = Files.readAllBytes(noticeFile).length >= 2
                    ? new byte[]{Files.readAllBytes(noticeFile)[0], Files.readAllBytes(noticeFile)[1]}
                    : new byte[0];

            try (InputStream is = Files.newInputStream(noticeFile)) {
                // 处理Gzip压缩文件
                if (header.length >= 2 && (header[0] & 0xFF) == 0x1F && (header[1] & 0xFF) == 0x8B) {
                    try (GZIPInputStream gzis = new GZIPInputStream(is);
                         BufferedReader reader = new BufferedReader(new InputStreamReader(gzis))) {
                        reader.lines().forEach(notices::add);
                    }
                } else {
                    // 处理普通文本文件
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
                        reader.lines().forEach(notices::add);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("处理 NOTICES 文件时发生错误: " + e.getMessage());
        }
        return notices;
    }

    // 优化：从内存中的字节数组提取字符串
    private List<String> extractStringsFromBinaryInMemory(byte[] fileData) {
        Set<String> strings = new LinkedHashSet<>();

        if (fileData == null) return new ArrayList<>(strings);

        try {
            // 优先使用系统命令（非Windows系统）
            if (!System.getProperty("os.name").toLowerCase().contains("win")) {
                // 创建临时文件用于strings命令
                Path tempFile = Files.createTempFile("libapp", ".so");
                try {
                    Files.write(tempFile, fileData);
                    Process process = new ProcessBuilder("strings", "-n", "4", tempFile.toString()).start();
                    try (BufferedReader reader = new BufferedReader(
                            new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
                        String line;
                        while ((line = reader.readLine()) != null) {
                            if (!line.trim().isEmpty()) {
                                strings.add(line);
                            }
                        }
                    }
                    if (process.waitFor() == 0) return new ArrayList<>(strings);
                } finally {
                    Files.deleteIfExists(tempFile);
                }
            }
        } catch (Exception e) {
            System.out.println("执行strings命令失败，回退到Java解析: " + e.getMessage());
        }

        // 纯Java实现（优化后的流式处理）
        try (ByteArrayInputStream bis = new ByteArrayInputStream(fileData)) {
            // 处理UTF-8字符串（优化版）
            byte[] buffer = new byte[4096];
            int bytesRead;
            StringBuilder currentString = new StringBuilder();
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                for (int i = 0; i < bytesRead; i++) {
                    char c = (char) (buffer[i] & 0xFF);
                    // 优化可打印字符判断逻辑
                    if (Character.isISOControl(c) || !Character.isDefined(c)) {
                        if (currentString.length() > 3) {
                            strings.add(currentString.toString());
                        }
                        currentString.setLength(0);
                    } else {
                        currentString.append(c);
                    }
                }
            }

            // 处理UTF-16字符串（优化版）
            bis.reset();
            currentString.setLength(0);
            byte[] utf16Buffer = new byte[4096];  // 更大的缓冲区
            while ((bytesRead = bis.read(utf16Buffer)) != -1) {
                for (int i = 0; i < bytesRead - 1; i += 2) {  // 成对处理字节
                    try {
                        char c = (char) ((utf16Buffer[i] & 0xFF) << 8 | (utf16Buffer[i+1] & 0xFF));
                        if (Character.isISOControl(c) || !Character.isDefined(c)) {
                            if (currentString.length() > 3) {
                                strings.add(currentString.toString());
                            }
                            currentString.setLength(0);
                        } else {
                            currentString.append(c);
                        }
                    } catch (ArrayIndexOutOfBoundsException e) {
                        // 忽略不完整的字节对
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("提取字符串时发生错误: " + e.getMessage());
        }

        return new ArrayList<>(strings);
    }

    // 保留原有方法以兼容性
    private List<String> extractStringsFromBinary(String filePath) {
        Set<String> strings = new LinkedHashSet<>();

        try {
            // 优先使用系统命令（非Windows系统）
            if (!System.getProperty("os.name").toLowerCase().contains("win")) {
                Process process = new ProcessBuilder("strings", "-n", "4", filePath).start();
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
                    String line;
                    while ((line = reader.readLine()) != null) {  // 更高效的流式读取
                        if (!line.trim().isEmpty()) {
                            strings.add(line);
                        }
                    }
                }
                if (process.waitFor() == 0) return new ArrayList<>(strings);
            }
        } catch (Exception e) {
            System.out.println("执行strings命令失败，回退到Java解析: " + e.getMessage());
        }

        // 纯Java实现（优化后的流式处理）
        try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(Path.of(filePath)))) {
            bis.mark(Integer.MAX_VALUE);  // 允许重置整个流
            
            // 处理UTF-8字符串（优化版）
            byte[] buffer = new byte[4096];
            int bytesRead;
            StringBuilder currentString = new StringBuilder();
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                for (int i = 0; i < bytesRead; i++) {
                    char c = (char) (buffer[i] & 0xFF);
                    // 优化可打印字符判断逻辑
                    if (Character.isISOControl(c) || !Character.isDefined(c)) {
                        if (currentString.length() > 3) {
                            strings.add(currentString.toString());
                        }
                        currentString.setLength(0);
                    } else {
                        currentString.append(c);
                    }
                }
            }

            // 处理UTF-16字符串（优化版）
            bis.reset();
            currentString.setLength(0);
            byte[] utf16Buffer = new byte[4096];  // 更大的缓冲区
            while ((bytesRead = bis.read(utf16Buffer)) != -1) {
                for (int i = 0; i < bytesRead - 1; i += 2) {  // 成对处理字节
                    try {
                        char c = (char) ((utf16Buffer[i] & 0xFF) << 8 | (utf16Buffer[i+1] & 0xFF));
                        if (Character.isISOControl(c) || !Character.isDefined(c)) {
                            if (currentString.length() > 3) {
                                strings.add(currentString.toString());
                            }
                            currentString.setLength(0);
                        } else {
                            currentString.append(c);
                        }
                    } catch (ArrayIndexOutOfBoundsException e) {
                        // 忽略不完整的字节对
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("提取 " + filePath + " 的字符串时发生错误: " + e.getMessage());
        }

        return new ArrayList<>(strings);
    }

    private Map<String, String> findDependencies(List<String> strings, List<String> notices, Set<String> knownPackages) {
        Map<String, String> dependencies = new HashMap<>();
    
        // 合并字符串和NOTICES内容
        List<String> allContents = new ArrayList<>(strings);
        allContents.addAll(notices);
    
        for (String line : allContents) {
            Matcher matcher = DEPENDENCY_PATTERN.matcher(line);
            while (matcher.find()) {
                String packageName = matcher.group(1);
                String version = matcher.group(2) != null ? matcher.group(2) : "unknown";
                
//                if (knownPackages.contains(packageName)) {
                    dependencies.put(packageName, version);
//                }
            }
        }
        return dependencies;
    }
}